﻿Imports System.Net
Imports System.Xml
Imports System.Security.Cryptography.X509Certificates
Imports System.Net.Security

Namespace TweeVo
	<Serializable> _
	Public Class TiVo
		' IP of TiVo
		Private privateIpAddress As IPAddress
		Public Property IpAddress() As IPAddress
			Get
				Return privateIpAddress
			End Get
			Set(ByVal value As IPAddress)
				privateIpAddress = value
			End Set
		End Property

		' Name of the TiVo machine assigned on the TiVo website
		Private privateMachine As String
		Public Property Machine() As String
			Get
				Return privateMachine
			End Get
			Set(ByVal value As String)
				privateMachine = value
			End Set
		End Property

		' unique identifier for the TiVo
		Private privateIdentity As String
		Public Property Identity() As String
			Get
				Return privateIdentity
			End Get
			Set(ByVal value As String)
				privateIdentity = value
			End Set
		End Property

		' platform of the TiVo (i.e. whether it's a TiVo or TiVo Desktop or something else)
		Private privatePlatform As String
		Public Property Platform() As String
			Get
				Return privatePlatform
			End Get
			Set(ByVal value As String)
				privatePlatform = value
			End Set
		End Property

		' was it selected by the user to be polled
		Private privateActive As Boolean
		Public Property Active() As Boolean
			Get
				Return privateActive
			End Get
			Set(ByVal value As Boolean)
				privateActive = value
			End Set
		End Property

		' time this TiVo was last polled
		Private privateLastPolled As Date
		Public Property LastPolled() As Date
			Get
				Return privateLastPolled
			End Get
			Set(ByVal value As Date)
				privateLastPolled = value
			End Set
		End Property

		Public Function GetNowPlayingList() As List(Of NPLEntry)
			' pull the entire NPL
			Dim docNowPlaying As XDocument = GetNowPlayingListDocument("NowPlaying", True)

			' pull only the suggestion folder (NowPlaying/0 is the magic URL for the Suggestions folder)
			Dim docSuggestions As XDocument = GetNowPlayingListDocument("NowPlaying%2f0", False)

			If docNowPlaying.Root Is Nothing Then
				Return Nothing
			End If

			Dim ns As XNamespace = docNowPlaying.Root.Name.Namespace

			If ns Is Nothing Then
				Return Nothing
			End If

			' get a list of ProgramId's for just the suggestions
			Dim querySuggestions = From entry In docSuggestions.Descendants(ns + "Item") _
			                       Let details = entry.Element(ns + "Details") _
			                       Where details.Element(ns + "ProgramId") IsNot Nothing AndAlso (Not details.Element(ns + "ProgramId").Value.StartsWith("TS")) AndAlso details.Element(ns + "CaptureDate") IsNot Nothing _
			                       Select CStr(details.Element(ns + "ProgramId"))
			Dim suggestionIds As List(Of String) = querySuggestions.ToList()

			' now pull the entire list of NPL entries that are real shows
			Dim query = From entry In docNowPlaying.Descendants(ns + "Item") _
			            Let details = entry.Element(ns + "Details") _
			            Where details.Element(ns + "ProgramId") IsNot Nothing AndAlso (Not details.Element(ns + "ProgramId").Value.StartsWith("TS")) AndAlso details.Element(ns + "CaptureDate") IsNot Nothing _
			            Order By details.Element(ns + "CaptureDate").Value Ascending _
			            Select New NPLEntry With {.Title = CStr(details.Element(ns + "Title")), .EpisodeTitle = CStr(details.Element(ns + "EpisodeTitle")), .SourceChannel = CStr(details.Element(ns + "SourceChannel")), .SourceStation = CStr(details.Element(ns + "SourceStation")), .CaptureDate = (details.Element(ns + "CaptureDate").Value).EpochToDateTime().RoundToNearestMinute(), .ProgramID = CStr(details.Element(ns + "ProgramId")), .Suggestion = suggestionIds.Exists(Function(s) s = CStr(details.Element(ns + "ProgramId")))}
								' if the program ID lives in the suggestion list, than mark it as a suggestion

			Dim entries As List(Of NPLEntry) = query.ToList()
			Return entries
		End Function

		Private Function GetNowPlayingListDocument(ByVal container As String, ByVal recurse As Boolean) As XDocument
			Dim request As HttpWebRequest
			Dim response As WebResponse = Nothing
			Dim doc As XDocument

			' pull the NPL
			Dim uri As String = String.Format("https://{0}/TiVoConnect?Command=QueryContainer&Container=%2F{1}&Recurse={2}", IpAddress, container, (If(recurse, "Yes", "No")))
			Logger.Log("Pulling " & uri & " from " & Machine & ", " & IpAddress.ToString() & ", Last polled: " & LastPolled, LoggerSeverity.Info)

			Try
				request = CType(WebRequest.Create(uri), HttpWebRequest)
				request.Credentials = New NetworkCredential("tivo", TweeVoSettings.Default.MediaAccessKey.DecryptString())
				' accept any ssl certificate
				ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf AcceptAll)

				response = request.GetResponse()
				Logger.Log("List retrieved", LoggerSeverity.Info)

				Dim xmlReader As XmlReader = XmlReader.Create(response.GetResponseStream())
				doc = XDocument.Load(xmlReader)
			Finally
				If response IsNot Nothing Then
					response.Close()
				End If
			End Try

			Return doc
		End Function

		Private Function AcceptAll (ByVal sender As Object, ByVal certificate As X509Certificate, ByVal chain As X509Chain, ByVal sslpolicyerrors As SslPolicyErrors) As Boolean
			Return True
  		End Function

		Public Function VerifyMAK(ByVal mak As String) As Boolean
			Dim request As HttpWebRequest
			Dim response As WebResponse = Nothing

			' run the QueryServer method...returns a short document
			Dim uri As String = String.Format("https://{0}/TiVoConnect?Command=QueryServer", IpAddress)
			Logger.Log("Verifying MAK " & mak & " on " & Machine & ", " & IpAddress.ToString(), LoggerSeverity.Info)

			Try
				request = CType(WebRequest.Create(uri), HttpWebRequest)
				request.Credentials = New NetworkCredential("tivo", TweeVoSettings.Default.MediaAccessKey.DecryptString())
				ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf AcceptAll)

				' if we get this far, we had a successful request
				response = request.GetResponse()
				Logger.Log("MAK verified", LoggerSeverity.Info)
			Catch ex As WebException
				If (CType(ex.Response, HttpWebResponse)).StatusCode = HttpStatusCode.Unauthorized Then
					Logger.Log("MAK invalid", LoggerSeverity.Info)
					Return False
				End If
			Finally
				If response IsNot Nothing Then
					response.Close()
				End If
			End Try

			Return True
		End Function

		Public Overrides Function ToString() As String
			Return Machine & " (" & IpAddress.ToString() & ")"
		End Function
	End Class
End Namespace